#include "MultiRing_Test.h"

#include "server/net/NetworkControl.h"
#include "server/multiProtocol/multiRing.h"
#include "Poco/Exception.h"
#include <sstream>
#include <iostream>
#include <iomanip>
#include <string.h>
#include <string>
/*********************************************************/
std::string MultiRingTest::name()
{
	return("MultiRing");
}
/*********************************************************/
bool MultiRingTest::runUnitTests()
{
	bool result = true;

	if ( !multiRingSerializeTest() ) result = false;
	if ( !multiRingDataUpdateTest() ) result = false;
	if ( !multiRingDataSortTest() ) result = false;
	if ( !multiRingDoubleExecutionTest() ) result = false;

	return( result ) ;
}
/*********************************************************/
bool MultiRingTest::multiRingDoubleExecutionTest()
{
	std::cout << "Running: multiRingDoubleExecutionTest()";
	std::cout.flush();

	try {
		NetworkControl& nc = NetworkControl::Instance();
		nc.start();
	
		MultiRing	mRing;
		mRing.setHash("9999988877632534");
		mRing.setPredecessorAddr("0.0.0.0:6000");
		mRing.setSuccessorAddr("0.0.0.0:6000");
		mRing.setAddr("0.0.0.0:6000");
		mRing.execute();
		mRing.execute();

		// Build the required string for comparison
		std::ostringstream	 ostr;

		ostr << "Ring in multicastgroup: " << "239.0.0.1:5001" << std::endl;
		ostr << "--------------------------------------" << std::endl;
		ostr << std::setw(25) << std::left << "Addr" << std::setw(25) << "Pred" << std::setw(25) << "Succ" << std::setw(40) << "Hash" << std::endl;
		ostr << std::setw(25) << "0.0.0.0:6000" << std::setw(25) << "0.0.0.0:6000" << std::setw(25) << "0.0.0.0:6000" << std::setw(40) << "9999988877632534" << std::endl;

		// Check if the ring message was correclty stored in the network control class
		if ( strcmp(ostr.str().c_str(), nc.getRingInformation().c_str()) == 0 ) {
			nc.stop();
			std::cout << "... success!" << std::endl;
			return true;
		} else {
			std::cout << "... failed! Invalid ring information stored in NetworkControl" << std::endl;
			std::cout << "NetworkControl returned: " << std::endl;
			std::cout << nc.getRingInformation() << std::endl;
			std::cout << "Expected: " << std::endl;
			std::cout << ostr.str() << std::endl;
			nc.stop();
			return false;
		}
		
		nc.stop();
	} catch(...) {
		std::cout << "... failed! Caught unknown exception, expected none" << std::endl;
		return false;
	}

	std::cout << "... failed!" << std::endl;
	return false;
}
/*********************************************************/
bool MultiRingTest::multiRingDataSortTest()
{
	std::cout << "Running: multiRingDataSortTest()";
	std::cout.flush();

	try { 
		NetworkControl& nc = NetworkControl::Instance();
		nc.start();

		MultiRing	mRing;
		mRing.setHash("9999988877632534");
		mRing.setPredecessorAddr("127.0.0.1:5001");
		mRing.setSuccessorAddr("127.0.0.1:5000");
		mRing.setAddr("0.0.0.0:6000");
		mRing.execute();

		mRing.setHash("EF1234");
		mRing.setAddr("0.0.0.0:6001");
		mRing.execute();

		mRing.setHash("FFFFFFFFFFFFFFFFFFF");
		mRing.setAddr("0.0.0.0:6002");
		mRing.execute();

		mRing.setHash("FFFFFFFFFFFFFFF1");
		mRing.setAddr("0.0.0.0:6003");
		mRing.execute();

		// Build the required string for comparison
		std::ostringstream	 ostr;

		ostr << "Ring in multicastgroup: " << "239.0.0.1:5001" << std::endl;
		ostr << "--------------------------------------" << std::endl;
		ostr << std::setw(25) << std::left << "Addr" << std::setw(25) << "Pred" << std::setw(25) << "Succ" << std::setw(40) << "Hash" << std::endl;
		ostr << std::setw(25) << "0.0.0.0:6001" << std::setw(25) << "127.0.0.1:5001" << std::setw(25) << "127.0.0.1:5000" << std::setw(40) << "EF1234" << std::endl;
		ostr << std::setw(25) << "0.0.0.0:6000" << std::setw(25) << "127.0.0.1:5001" << std::setw(25) << "127.0.0.1:5000" << std::setw(40) << "9999988877632534" << std::endl;
		ostr << std::setw(25) << "0.0.0.0:6003" << std::setw(25) << "127.0.0.1:5001" << std::setw(25) << "127.0.0.1:5000" << std::setw(40) << "FFFFFFFFFFFFFFF1" << std::endl;
		ostr << std::setw(25) << "0.0.0.0:6002" << std::setw(25) << "127.0.0.1:5001" << std::setw(25) << "127.0.0.1:5000" << std::setw(40) << "FFFFFFFFFFFFFFFFFFF" << std::endl;

		// Check if the ring message was correclty stored in the network control class
		if ( strcmp(ostr.str().c_str(), nc.getRingInformation().c_str()) == 0 ) {
			nc.stop();
			std::cout << "... success!" << std::endl;
			return true;
		} else {
			std::cout << "... failed! Invalid ring information stored in NetworkControl" << std::endl;
			std::cout << "NetworkControl returned: " << std::endl;
			std::cout << nc.getRingInformation() << std::endl;
			std::cout << "Expected: " << std::endl;
			std::cout << ostr.str() << std::endl;
			nc.stop();
			return false;
		}
		
		nc.stop();
	} catch(...) {
		std::cout << "... failed! Caught exception, expected none" << std::endl;
		return false;
	}

	std::cout << "... failed!" << std::endl;
	return ( false );
}
/*********************************************************/
bool MultiRingTest::multiRingSerializeTest()
{
	std::cout << "Running: multiRingSerializeTest()";
	std::cout.flush();

	try {
		MultiRing	mRing;
		mRing.setHash("8888888877777777");
		mRing.setPredecessorAddr("127.0.0.1:5001");
		mRing.setSuccessorAddr("127.0.0.1:5000");
		mRing.setAddr("127.0.0.1:6000");

		std::string xml = mRing.serialize();

		if ( strcmp(xml.c_str(), "<MGS type='RING'><hash>8888888877777777</hash><pred>127.0.0.1:5001</pred><succ>127.0.0.1:5000</succ><addr>127.0.0.1:6000</addr></MGS>") != 0 ) {
			std::cout << "... failed! Unexpected output from serialize!" << std::endl;
			return false;
		}
	} catch(...) {

	}
	
	std::cout << "... success!" << std::endl;
	return true;
}
/*********************************************************/
bool MultiRingTest::multiRingDataUpdateTest()
{
	std::cout << "Running: multiRingDataUpdateTest()";
	std::cout.flush();

	try {
		NetworkControl& nc = NetworkControl::Instance();
		nc.start();

		MultiRing	mRing;
		mRing.setHash("E98123EBCD");
		mRing.setPredecessorAddr("127.0.0.1:1000");
		mRing.setSuccessorAddr("127.0.0.1:1000");
		mRing.setAddr("0.0.0.0:6000");
		mRing.execute();

		// Build the required string for comparison
		std::ostringstream	 ostr;

		ostr << "Ring in multicastgroup: " << "239.0.0.1:5001" << std::endl;
		ostr << "--------------------------------------" << std::endl;
		ostr << std::setw(25) << std::left << "Addr" << std::setw(25) << "Pred" << std::setw(25) << "Succ" << std::setw(40) << "Hash" << std::endl;
		ostr << std::setw(25) << "0.0.0.0:6000" << std::setw(25) << "127.0.0.1:1000" << std::setw(25) << "127.0.0.1:1000" << std::setw(40) << "E98123EBCD" << std::endl;

		// Check if the ring message was correclty stored in the network control class
		if ( strcmp(ostr.str().c_str(), nc.getRingInformation().c_str()) == 0 ) {
			nc.stop();
			std::cout << "... success!" << std::endl;
			return true;
		} else {
			std::cout << "... failed! Invalid ring information stored in NetworkControl" << std::endl;
			std::cout << "NetworkControl returned: " << std::endl;
			std::cout << nc.getRingInformation() << std::endl;
			std::cout << "Expected: " << std::endl;
			std::cout << ostr.str() << std::endl;
			nc.stop();
			return false;
		}
	} catch(Poco::Exception& e) {
		std::cout << "... failed! Caught exception: " << e.displayText() << ". No exception expected!" << std::endl;
		return false;
	} catch(std::exception& e) {
		std::cout << "... failed! Caught exception: " << e.what() << ". No exception expected!" << std::endl;
		return false;
	} catch(const char* e) {
		std::cout << "... failed! Caught exception: " << e << ". No exception expected!" << std::endl;
		return false;
	} catch(...) {
		std::cout << "... failed! Caught exception, expected none" << std::endl;
		return false;
	}

	std::cout << "... failed!" << std::endl;
	return ( false );
}
/*********************************************************/

